// CH5_1.cpp
// by Charles Mc Auley
// "Programming AutoCAD 2000 with ObjectARX"
//
// This application draws three kinds of windows.
// The window styles are Arch, Apex and Rectangular.
// This application define 3 window type classes.
// The base class is RectWindow and the derived
// classes are ArchWindow and ApexWindow. We also
// demonstrate Object Oriented techniques and code reuse.
// This application also does more error checking that before.
// This application also make use of ObjectARX's geometry classes.
//
/////////////////////////////////////////////////////////


#include <acdb.h>		// acdb definitions
#include <adslib.h>		// ads defs
#include <rxregsvc.h>	// unlock application
#include <aced.h>
#include <dbsymtb.h>	// Symbol Tables
#include <geassign.h>	// for asDblArray()
#include <dbapserv.h>	// acdbHostApplicationServices()


// TODO: add your own includes

#include "RectWindow.h"	// RectWindow class
#include "ArchWindow.h"	// ArchWindow class
#include "ApexWindow.h"	// ApexWindow Class

// Function prototypes
// entry point for this application
extern "C" AcRx::AppRetCode acrxEntryPoint( AcRx::AppMsgCode msg, void* );

// helper functions
void initApp  (void);
void unloadApp(void);

// user defined functions
void windo();
Adesk::Boolean getWindowInputParameters(RectWindow* pWindow);
Adesk::Boolean getModelSpaceRecord(AcDbBlockTableRecord*& pBlkTableRecord);

/////////////////////////////////////////////////////////////////////
// acrxEntryPoint(internal)
// This function is the entry point for your application.
/////////////////////////////////////////////////////////////////////
AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* ptr)
{
	switch (msg) {
		case AcRx::kInitAppMsg :
			acrxUnlockApplication(ptr);
			acrxRegisterAppMDIAware(ptr);
			initApp();
		break;

		case AcRx::kUnloadAppMsg :
			unloadApp();
		break;

		default:
		break;
	} // switch

  return AcRx::kRetOK;
}

void initApp(void)
{
	// TODO: init your application
    // register a command with the AutoCAD command mechanism

    acedRegCmds->addCommand("CH5_APPS", "WINDO", "WINDO",
                    ACRX_CMD_MODAL, windo);
	acutPrintf("Enter \"WINDO\" to execute the application.\n");

}

void unloadApp(void)
{
	// TODO: clean up your application
    // Remove the command group added via acedRegCmds->addCommand

	acutPrintf("%s%s", "Goodbye\n", "Removing command group \"CH5_APPS\"\n");
    acedRegCmds->removeGroup("CH5_APPS");

}

// TODO: add your other functions here
void windo()
{
	AcDbBlockTableRecord *pBlkTableRecord;

	char kw[20];
	int rc;	// Return code

	RectWindow *pRectWindow;	// RectWindow class
	ArchWindow *pArchWindow;	// Derived ArchWindow class
	ApexWindow *pApexWindow;	// Derived ApexWindow class
	RectWindow *pGenericWind;
	
	acedInitGet(NULL, "Arch apeX Rect");
	rc = acedGetKword("\nSelect window type - [Arch/apeX/Rect]<Rect>: ", kw);

	switch(rc)
	{
		case RTCAN:
			acutPrintf("\nUser cancelled. ");
			return;
		break;

		case RTERROR:
			acutPrintf("\nError with selection type. ");
			return;
		break;

		case RTNONE:
			strcpy(kw, "Rect");
			rc = RTNORM;

			pRectWindow = new RectWindow;
			pGenericWind = pRectWindow;

		break;

		case RTNORM:
			if(strcmp(kw, "Arch") == 0)
			{
				pArchWindow = new ArchWindow;
				pGenericWind = pArchWindow;
			}// if
			else if(strcmp(kw, "Rect") == 0)
			{
				pRectWindow = new RectWindow;
				pGenericWind = pRectWindow;
			}//else if
			else if(strcmp(kw, "apeX") == 0)
			{
				pApexWindow = new ApexWindow;
				pGenericWind = pApexWindow;
			}
			else
			{
				acutPrintf("\nError in retrieving keyword. ");
				return;
			}// else
		break;

	}// switch

	if(!getWindowInputParameters(pGenericWind))
	{
		acutPrintf("\nError in window input parameters. ");
		if(pGenericWind != NULL)
		{
			delete pGenericWind;
		}
		return;
	}

	if(!getModelSpaceRecord(pBlkTableRecord))
	{
		return;
	}

	pGenericWind->drawWindow(pBlkTableRecord);

	if(pGenericWind != NULL)
	{
		delete pGenericWind;
	}

	pBlkTableRecord->close();
}

Adesk::Boolean getWindowInputParameters(RectWindow* pWindow)
{
	AcGePoint2d startPt;
	AcGePoint3d pickPt;
	int rows = 1, cols = 1;
	double windLength, windHeight;
	int rc;	// Return code

	acedInitGet(RSG_NONULL + RSG_NONEG + RSG_NOZERO, NULL);
	rc = acedGetReal("\nWindow Height: ", &windHeight);

	if(rc == RTCAN || rc == RTERROR)
	{
		return Adesk::kFalse;
	}

	acedInitGet(RSG_NONULL + RSG_NONEG + RSG_NOZERO, NULL);
	rc = acedGetReal("\nWindow Length: ", &windLength);

	if(rc == RTCAN || rc == RTERROR)
	{
		return Adesk::kFalse;
	}

	acedInitGet(RSG_NOZERO + RSG_NONEG, NULL);
	rc = acedGetInt("\nNumber of window columns <Return> for none: ", &cols);
	
	if(rc == RTCAN || rc == RTERROR)
	{
		return Adesk::kFalse;
	}

	acedInitGet(RSG_NOZERO + RSG_NONEG, NULL);
	rc = acedGetInt("\nNumber of window rows <Return> for none: ", &rows);
	
	if(rc == RTCAN || rc == RTERROR)
	{
		return Adesk::kFalse;
	}


	acedInitGet(NULL, NULL);
	rc = acedGetPoint(NULL, "\nPick lower left window insertion point <Return for (0,0)>: ", 
				asDblArray(pickPt));

	
	switch(rc)
	{
		case RTCAN:
		case RTERROR:
			return Adesk::kFalse;
		break;

		case RTNONE:
			startPt.x = 0;
			startPt.y = 0;
		break;

		case RTNORM:
			startPt.x = pickPt.x;
			startPt.y = pickPt.y;
		break;
	}

	// Fill out the window class object
	// with the input values

	pWindow->setWindowLength(windLength);
	pWindow->setWindowHeight(windHeight);
	pWindow->setWindowCols(cols);
	pWindow->setWindowRows(rows);
	pWindow->setWindowStartPoint(startPt);

	return Adesk::kTrue;
}

Adesk::Boolean getModelSpaceRecord(AcDbBlockTableRecord*& pBlkTableRecord)
{
	AcDbDatabase *pCurDb;
	AcDbBlockTable *pBlkTable;
	Acad::ErrorStatus es;

	pCurDb = acdbHostApplicationServices()->workingDatabase();

	es = pCurDb->getBlockTable(pBlkTable, AcDb::kForRead);
	if(es != Acad::eOk)
	{
		acutPrintf("\nFailed to open Block Table for a read operation.");
		return Adesk::kFalse;
	}

	es = pBlkTable->getAt(ACDB_MODEL_SPACE, pBlkTableRecord, AcDb::kForWrite);
	if(es != Acad::eOk)
	{
		acutPrintf("\nFailed to open MODEL SPACE for a write operation.");
		pBlkTable->close();
		return Adesk::kFalse;
	}

	// We don't need the block table anymore so we can close it.
	pBlkTable->close();

	return Adesk::kTrue;
}